package com.phenix.network.response

import com.phenix.network.base.Envelope
import com.phenix.network.error.BusinessException
import com.phenix.network.util.MoshiUtils
import okhttp3.Request
import okio.IOException
import okio.Timeout
import org.json.JSONObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class NetworkResponseCall<S : Any>(
    private val delegate: Call<S>,
    private val failureHandler: NetworkResponseAdapterFactory.FailureHandler?,
    private val envelope: Envelope
) : Call<NetworkResponse<S>> {

    override fun enqueue(callback: Callback<NetworkResponse<S>>) {
        return delegate.enqueue(object : Callback<S> {
            override fun onResponse(call: Call<S>, response: Response<S>) {
                val body = response.body()
                val code = response.code()
                val error = response.errorBody()

                if (response.isSuccessful) {
                    // 200x
                    if (body != null) {
                        callback.onResponse(
                            this@NetworkResponseCall,
                            Response.success(NetworkResponse.Success(body))
                        )
                    } else {
                        callback.onResponse(
                            this@NetworkResponseCall,
                            Response.success(NetworkResponse.UnknownError(null))
                        )
                    }
                } else {
                    if (error != null && error.contentLength() > 0) {
                        // 500x
                        val errorJson = MoshiUtils.fromJson<JSONObject>(
                            error.toString(),
                            JSONObject::class.java
                        )

                        val errorCode: Int = errorJson?.getInt(envelope.getStatusCodeKey()) ?: -1
                        val errorMsg: String =
                            errorJson?.getString(envelope.getErrorMessageKey()) ?: ""

                        failureHandler?.onFailure(
                            BusinessException(
                                errorCode,
                                errorMsg
                            )
                        )

                        callback.onResponse(
                            this@NetworkResponseCall,
                            Response.success(
                                NetworkResponse.ApiError(
                                    errorMsg,
                                    errorCode
                                )
                            )
                        )
                    } else {
                        callback.onResponse(
                            this@NetworkResponseCall,
                            Response.success(
                                NetworkResponse.NetworkError(
                                    error?.toString() ?: "message is empty",
                                    code
                                )
                            )
                        )
                    }
                }
            }

            override fun onFailure(call: Call<S>, t: Throwable) {
                if (t is BusinessException && failureHandler?.onFailure(t) == true) {
                    return
                }

                callback.onResponse(
                    this@NetworkResponseCall,
                    Response.success(
                        when (t) {
                            is IOException -> NetworkResponse.NetworkError(
                                t.message.toString(),
                                400
                            )
                            else -> NetworkResponse.UnknownError(t)
                        }
                    )
                )
            }

        })
    }

    override fun clone(): Call<NetworkResponse<S>> =
        NetworkResponseCall(delegate.clone(), failureHandler, envelope)

    override fun execute(): Response<NetworkResponse<S>> {
        throw UnsupportedOperationException("NetworkResponseCall doesn't support execute")
    }

    override fun isExecuted(): Boolean = delegate.isExecuted

    override fun cancel() = delegate.cancel()

    override fun isCanceled(): Boolean = delegate.isCanceled

    override fun request(): Request = delegate.request()

    override fun timeout(): Timeout = delegate.timeout()
}